home *** CD-ROM | disk | FTP | other *** search
-
-
-
-
-
-
-
-
-
-
-
-
-
- ON PORTING MAELSTROM TO LINUX
-
-
- by
- Sam Lantinga
-
-
-
-
-
- Graphics:
-
- X11 was originally designed as a well defined client-server
- protocol, device independent, network capable windowing system. It was
- fully capable of supporting the applications of the time: menus, text
- editors, simple bitmap graphics, etc. However, many applications today
- require a faster, more responsive graphics interface. Any real-time video
- application requires much faster video access and updates than the X11
- system was originally designed to provide. Examples of these types of
- applications include teleconferencing systems, MPEG video players, real-time
- modeling object modeling, etc. The pioneers in real-time audio/visual
- techniques have been (and probably will be) fast-action video games. Arcade
- games stress hardware more than almost any other application, requiring
- real-time video, real-time audio, and good response time to user input.
- More advanced games run networked as well, requiring fast, well designed
- transmission control protocol and routing of network packets (not discussed
- in this paper.)
-
- These real-time services are easy to provide in a simple,
- single-user system such as the DOS PC, making it one of the most popular
- platforms for video games. It is slightly more complex to provide these in
- a well designed single-user windowing system such as the Macintosh.
- However, even there, it is possible to give full access to the hardware to
- any applications that require it. This performance is much harder to
- provide in a multi-user time-sharing environment such as UNIX X11.
-
-
- The objective of this project was to show that fast-action games,
- and hence, other high-speed interactive applications, can be written for the
- X11 environment. The target application for this project was the Macintosh
- shareware game "Maelstrom". From the press release:
-
- "Maelstrom 1.4 is a marriage of the venerable Asteroids concept
- with new digitized sound effects, 3D graphics and high resolution
- 256 color animation. 'With fantastic art and graphics by Ian
- Gilman and Mark Lewis, Maelstrom does for the tired old Asteroids-
- style games what the Mazda Miata did for sporty roadsters...'"
-
- As such, Maelstrom seemed the perfect target for the X11 environment.
- The operating system chosen for the port was Linux version 1.2.9, a free
- UNIX-like operating system for the PC. The development hardware was a
- DX2/66 VLB PC with a Cirrus Logic 5426 video board and a Sound-Blaster Pro
- 8-bit sound-card. The X11 environment used was XFree86 version 3.1, using
- the SVGA X server with the built in MITSHM extension.
-
- After obtaining the source code through a non-disclosure agreement
- with the author, Andrew Welch, I began the port. The most immediate
- problems I ran into had nothing to do with graphics speed, or hardware
- access. How was I going to be able to get the icons, sprites and sounds out
- of the Macintosh resource fork files? Throughout the port, whenever I would
- run into these problems, there would be some resource, some paper or program
- written that would help me solve the problem. A (nearly) complete list of
- these and credits are in an appendix to this paper. For the specs on the
- Macintosh resource files, I went to the well done series "Inside Macintosh",
- published by Addison and Wesley. With the specs in hand, I coded C++
- classes that could parse Macintosh resource forks (e.g. dialog box
- specifications, custom fonts, sprites, icons, sound clips, etc.) directly
- from the UNIX file-system. How was I able to get the resource forks there
- in the first place? That was made possible by the new HFS file-system
- driver for Linux, written by Paul Hargrove. Once I had routines that could
- extract sprites, color icons, and color-maps, I went to work on the graphics
- interface.
-
- I had a game for X11, called "Xboing" written by an Australian
- Justin Kibell, which used the Xpm library for full color animation in a
- well designed blockout-style game. (For more information, his WWW page is
- http://144.110.160.105:9000/~jck/xboing/xboing.html) However, using the
- Xpm library, copying pixmap sprites from application to X server, his
- animation has a great deal of "flicker" where the animation seems to flash
- as it goes along. His method of dealing with sounds (designed for the
- sun /dev/audio interface) is not tightly synchronized, leading to sounds
- and animation for a single event occurring several seconds apart. The effect
- is similar to that of a high flying jet -- the sight of the jet precedes
- the sound of the jet by several seconds.
-
- Another game, recently ported to the Linux X11 environment, is
- the popular "DOOM!" game, by ID Software. DOOM! uses frame-buffer based
- animation, where multiple frames are blitted to the screen per second,
- giving the appearance of smoothly flowing graphics. The X11 port takes
- advantage of an extension to the X11 protocol known as "MITSHM", or
- "MIT Shared Memory Extension" in which the client application and the X
- server share a segment of memory that corresponds to an off-screen "image".
- Graphics are drawn to this off-screen image and a single call to the X11
- server tells it to copy the image to a window on the physical screen.
- This is much faster than sending each frame through the client-server
- communications connection. For a 320x200 resolution window, this results
- in reasonable performance. The game can be played smoothly and in this
- game, sound is tightly coupled to the actions in the game, for realistic
- play.
-
- In all but the fastest systems, the MITSHM off-screen image
- technique is limited to 320x200 resolution. Any larger, and the X server
- cannot copy the image to the window fast enough for smoothly flowing
- graphic frames. Most X systems run in higher resolution, turning the
- full-screen virtual reality game "DOOM!" into a tiny window on the desktop.
- Maelstrom was designed for 640x480 resolution, and uses moving sprite
- animation, so the MITSHM off-screen image technique is not suitable for the
- Maelstrom port. The Xpm method of erasing and drawing sprite images would
- be perfect for Maelstrom, if the images could be copied fast enough to
- provide smoothly moving animation. With sometimes upwards of 30 moving
- objects on the screen at once, the copying would have to be fast indeed.
-
- I combined both approaches. Using a technique I haven't heard of
- before, I used the feature that a pixmap can be used as a window background,
- in combination with the fact that a shared memory segment can be associated
- with a pixmap, to create a shared memory pixmap that was the background of
- a window! I can then directly manipulate the pixels in the background and
- simply call XClearArea() to refresh the changed area of the screen. This
- technique gives an incredible speedup over the shared memory image method,
- because the window is not modified by copying an entire frame, but by merely
- refreshing the existing one. All graphics drawing is done on the background
- of the window and then placed on the display by calling XClearArea(). This
- technique completely breaks down the client-server network relationship of
- the X11 environment, but for certain high-speed graphics applications, the
- speed increase is definitely worthwhile.
-
- Even with the speedy technique of shared background pixmaps, there
- needs to be some way of copying the original sprite pixel data into the
- shared background (hereafter referred to as the frame-buffer.) A technique
- suggested by Andrew Welch, was to compile the sprites into streams of pixels
- and opcodes. Instead of running a loop, copying each pixel independently
- after checking to see if it needs to be put on the screen, compiled sprites
- are streamed onto the frame-buffer using the largest copy possible, switching
- on the next opcode to see how big a skip to make, streaming a next copy,
- skipping, etc. Copying compiled sprites was 50 percent faster than using
- the old sprite/mask technique. An additional advantage was that compiled
- sprites took an average of 50 percent less storage space than the old
- sprites and masks. Compiled sprites ran into problems however, when
- clipping them on the edge of the screen. sprite pixel-maps and masks have
- the advantage that each row of pixels is a fixed width, and all you have to
- do is skip a certain amount into each row to clip an edge of the sprite.
- Each row of a compiled sprite is variable width, and the checking required
- to clip them would take longer than the pixel/mask method. I wrote a
- routine to dynamically recompile a compiled sprite, based on clipping
- conditions, but it too was slightly slower than the pixel/mask method. For
- sprites with absolute coordinate positions, I use the pixel/mask image
- updating during clipping and for small sprites with relative coordinate
- positions (thrust sprites, etc) I use compiled sprite recompiling during
- clipping. This combination of techniques resulted in a good graphics speed
- for high-speed play of the game Maelstrom.
-
- Color mapping is a big issue in the X11 environment. Since the
- screen is shared with other applications, and there are limited color
- entries in the color table, you need some way of getting the colors you
- need, without depriving other applications of the colors they need. There
- are several ways to approach this. "Netscape", a world wide web browser,
- just grabs all the free color cells and allocates the colors it wants.
- "xv", an image viewer, allocates an orthogonal spectrum of color cells and
- maps the colors it wants to this spectrum of colors. "DOOM!" allocates a
- private color-map, fills it with it's own colors and uses that. This has the
- side-effect of turning all other windows on the X display into psychedelic
- color friezes. Since Maelstrom uses a full 256 color table, it needs more
- colors than are generally free in a shared color-map. The approach I took
- was similar to that of xv -- I allocated a spectrum of colors, and then
- mapped the colors I wanted to this spectrum, plus the colors already in
- the colormap. A command-line option can be used to tell Maelstrom to
- allocate a private color-map for "true" colors, the same way "DOOM!" does.
-
- Color mapping is only an issue in 8-bit displays. Truecolor
- displays can display thousands or millions of colors simultaneously and
- require different color techniques. My hardware will only support 8-bit
- color, and so Maelstrom doesn't currently support Truecolor displays.
-
- The VGA port posed an interesting problem, compared to X11
- graphics. In the X11 paradigm, graphic updates are queued until explicitly
- flushed, or until the next call for input. Under SVGAlib, as soon as a
- graphics request is made, the screen is updated. This results in "flicker",
- as all of the sprites are erased, then moved, and then they are updated.
- My solution was to create an expandable stack of refresh updates, and then
- only update the screen when explicitly told to, or when asked for input.
- This gets rid of the flicker quite nicely. The SVGAlib version of Maelstrom
- is much smoother than the X11 version.
-
-
- Sound:
-
- The next component was sound. Dave Taylor, author of the Linux port
- of DOOM!, worked with the author of the VoxWare sound driver for Linux,
- enhancing the real-time sound capabilities of the sound driver. Recently,
- Terry Evans (tevans@cs.utah.edu) wrote a sound effects server for Linux that
- can mix multiple sounds. I adapted the algorithm for sound mixing for my
- own sound server for Maelstrom. Each loop of the mixer combines multiple
- channels of the sound mixer into a single chunk of sampled data which is
- sent to the audio device. The original idea was to have a continuous loop,
- first checking for input, and then writing a chunk of sound (or silence) to
- the sound device. This resulted in slow response time, because the a sound
- event had to wait the entire cycle of combining the sound channels until it
- could be acted upon. I modified the original concept to support
- asynchronous input. Now, the loop has been simplified to a simple
- continuous play of the sound channels, but at any time during the
- compilation of a sound chunk, new sound data can be placed into channels, or
- removed from the mixing channels. This allows nearly instantaneous mixing
- of sound effects, in response to sound events.
-
- I decided to run the sound server as a separate process from
- Maelstrom. The sound server has to continuously play sampled data (sound or
- silence) and respond instantly to sound events. Maelstrom has to
- continuously update animated graphics. I thought the best way to perform
- both of these functions simultaneously was to do them in separately running
- processes, communicating through a private UNIX domain socket. The socket
- is set up for asynchronous I/O, and when one process sends a message to the
- other, they are interrupted by a SIGIO signal. A handler is set up for each
- process, handling requests. The sound server handles sound requests, and
- sends a "sound done playing" message when it finishes playing a sound.
-
- This scheme works remarkably well, however there are still some
- problems with it. If the sound fragment size is too large, the write to the
- sound device returns too soon, and the time the sound is played doesn't sync
- with the time the program thinks the sound is played. On slow systems,
- sound requests can interrupt the write() system call, and if the requests
- come too quickly, a single write will never complete. The current
- implementation tests the write() return value for EINTR, signaling it was
- interrupted, and performs a goto to restart the write. It also has to
- re-set the signal handler at each interrupt, and can be interrupted during
- request processing. This can result in infinite recursion on the signal
- handler on slow systems (this has not been observed on my system).
-
- Regarding the accuracy of Maelstrom sound, there is a trade-off
- between the accuracy of the synchronization between Maelstrom and the sound
- server, and the smoothness of sound play. The fragment size needs to be
- large enough so that the audio device continues playing while the Maelstrom
- process runs, and small enough so that the sound write returns soon after a
- sound is finished playing, i.e. not too much extra sound being played in a
- chunk after then end of a sound clip. I've found that 1024 bytes is a fair
- number for the fragment size, weighing timing accuracy and smoothness of play.
- The only time the timing accuracy is really noticeable is at the end of the
- level in the bonus count-down screen.
-
-
- The Game:
-
- The main body of the game, all of the hit detection, sprite
- updating, movement, etc was originally written as approximately five
- thousand lines of 68K assembler. I rewrote and translated it all to
- fourteen hundred lines of in-lined C++. I did not change any of the logic of
- the game, intending this port to be as faithful a representation of the
- original as possible. The structure of the game was preserved as much as
- possible, changed only where the differences between the Macintosh and Linux
- environment required them. These changes were primarily in the graphics
- interface. Andrew Welch included the header file to his sound server, and
- I emulated his entry point functions (the API) in my C++ sound-client class.
-
- The Maelstrom dialog boxes were captured from the screen of a
- Macintosh using the System 7.5 screen capture facility (Command-Shift-3) and
- then analyzed at the pixel level using 'xv', and recreated with custom
- written Mac-like Dialog classes and a Font handling class that can translate
- text strings, with Macintosh 'NFNT' resources, directly into blittable
- bitmaps. They work almost 100% exactly like the originals.
-
- I thought about evolving some of the simple data structures used by
- Maelstrom, (e.g. arrays, sprite structs) into some of the more advanced data
- types, such as objects, supported by the C++ language. This would improve
- the _look_ of the code quite a bit, but would slow it down as well. If I
- were to do major redesigning of the way the game works, it might be
- worthwhile, but since it works well, and works fast, I would not want to add
- unnecessary data complexity. One concession I made to the C++
- object-oriented paradigm was implementing the graphics display driver
- as a "FrameBuf" framebuffer graphics class. I plugged in graphics modules
- for both X11 and SVGAlib so that the same executable program can run in
- both X Windows and Linux console environments.
-
- At this point, the Linux version of Maelstrom is Freeware, by
- permission of Andrew Welch, the author of the original Macintosh version.
- It includes source code for the Linux version, and boldly displays the
- Ambrosia Software logo at startup.
-
-
- Problems:
-
- When Maelstrom dies unexpectedly, it leaves shreds of shared memory
- lying around the system. These need to be removed by hand, or reclaimed by
- Maelstrom itself.
-
-
- Future Enhancements:
-
- I would like to find a way to reclaim shared memory that
- has been orphaned, possibly by using a Maelstrom-specific shared memory
- identifier. I will look at the source to "ipcs" to find out how to
- search out shared memory on a system.
-
- I would like to port Maelstrom to the SGI. A port to the SGI
- would be fairly simple, except for one thing. I know nothing about
- the SGI sound interface. All of the SGI systems I have access to do
- not have the sound interface documentation installed.
-
- I would like to eventually write an enhanced version of Maelstrom,
- with additional bonuses, "rubber" asteroids, etc. Maelstrom+? This would
- be in collaboration with Andrew Welch, and would require that I first...
-
- It would be nice to extend my Macintosh Resource class to be able
- to write the Macintosh resource forks as well as read them. I also want
- to expand my sound class to be able to understand other sound formats
- besides Macintosh sampled sound bites.
-
-
- Conclusion:
-
- Ya HOO!!! It can be done!! My friends have played it and
- pronounced it a very good job. As far as I can tell, the Linux version of
- Maelstrom is an authentic Maelstrom version, complete in every detail. The
- timing is accurate, response time is accurate, animation is smooth, sound is
- clear and timely.
-
- I feel that I have demonstrated that games of good quality,
- and real-time audio-visual applications of all kinds can be well written
- and well used in the Linux/X11 environment.
-
- Excuse me, I have to go play. :-)
-
-
-
-
-
-
-
- APPENDIX A: CREDITS
-
-
- Thanks to all the people who helped this project be fulfilled.....
-
- (a partial list follows)
-
- Emily, who reminds me to take care of myself, without whom, I would
- eat only chips and salsa, stay up until 5 A.M. every night, and sleep
- through all my classes. :)
-
- Ron Olsson, who's giving me credit for having fun. :)
-
- Andrew Welch, the author of Maelstrom
-
- Larry, for being picky, and praising perfection.
-
- Dave, for letting me use your Mac all the time.
-
- Whoever that C++ teacher was... ;-)
-
- Paul Hargrove, author of the HFS file-system for Linux
- (hargrove@sccm.stanford.edu)
-
- Justin Kibell for inspiring me to write a good game for Linux.
- (jck@citri.edu.au)
-
- Terry Evans, author of that great mixer, "sfxserver"
-
- The combined authors of "Inside Macintosh" -- invaluable!
-
- The author of ResEdit
-
- The author of GraphicsConverter for the Mac
-
- John Bradley, author of "xv"
-
- Dave Taylor, author of the "DOOM!" for Linux port, who first turned
- me on to MITSHM
-
- Guido van Rossum, a man named Guido who wrote "mac2bdf".
-
- The XFree86 Team, for a great X11 windowing system! :)
-
- Cliff at ARDI -- I still say mine is faster! ;-)
-
- Manuel, who first showed me Maelstrom -- I'm addicted! :)
-
-
- Of course, Linus and Linux-heads everywhere, I wouldn't have Linux
- without you....
-